package com.ymm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ymm.domain.dto.UserDto;
import com.ymm.domain.entity.*;
import com.ymm.domain.vo.ResponseResult;
import com.ymm.domain.vo.RouterVo;
import com.ymm.domain.vo.UserInfoVo;
import com.ymm.enums.AppHttpCodeEnum;
import com.ymm.enums.RedisKeyEnum;
import com.ymm.enums.SystemContains;
import com.ymm.exception.SystemException;
import com.ymm.service.*;
import com.ymm.utils.BeanCopyUtils;
import com.ymm.utils.JwtUtil;
import com.ymm.utils.RedisCache;
import com.ymm.utils.security.SecurityUtils;
import com.ymm.utils.tree.TreeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 椰咩咩咩
 * @version 1.0-SNAPSHOT
 * @date 2023/1/24 10:32
 * @Description
 */
@Service
@Transactional
public class LoginServiceImpl implements LoginService {

    @Value("${ymm.login.out-time}")
    private Integer loginOutTime;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private MenuService menuService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private RoleMenuService roleMenuService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private UserService userService;

    @Override
    public ResponseResult login(User user) {
        if (!StringUtils.hasText(user.getUserName())) {
            //提示 必须要传用户名
            throw new SystemException(AppHttpCodeEnum.REQUIRE_USERNAME);
        }
        if (!StringUtils.hasText(user.getPassword())) {
            //提示 必须要传用户名
            throw new SystemException(AppHttpCodeEnum.REQUIRE_PASSWORD);
        }

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication authentication = authenticationManager.authenticate(authenticationToken);

        // 判断认证是否通过
        if (Objects.isNull(authentication)) {
            throw new SystemException(AppHttpCodeEnum.LOGIN_ERROR);
        }
        // 获取UserID，生成token
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        if (loginUser.getUser().getStatus().equals(SystemContains.STATUS_ABNORMAL)) {
            throw new SystemException(AppHttpCodeEnum.USER_STATUS_ABNORMAL);
        }
        Long userid = loginUser.getUser().getId();
        String token = JwtUtil.createJWT(userid.toString(), loginOutTime.longValue());
        Map<String, String> map = new HashMap<>();
        map.put("token", token);
        // 将用户信息错放在redis里面当中
        redisCache.setCacheObject(RedisKeyEnum.HT_USER_KEY + userid, loginUser, loginOutTime, TimeUnit.MILLISECONDS);
        return ResponseResult.okResult(map);
    }

    @Override
    public ResponseResult getInfo() {
        User user = SecurityUtils.getLoginUser().getUser();
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
        return ResponseResult.okResult(userInfoVo);
    }

    @Override
    public ResponseResult logout() {
        Long userId = SecurityUtils.getUserId();
        //删除redis里面的用户信息
        redisCache.deleteObject(RedisKeyEnum.HT_USER_KEY + userId);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getRouters() {
        Long userId = SecurityUtils.getUserId();
        List<Long> rIds = userRoleService.getRIdByUserId(userId);
        if (rIds.size() <= 0) {
            return ResponseResult.okResult(new ArrayList<>());
        }
        List<Long> mIds = roleMenuService.getMidByRoles(rIds);
        if (mIds.size() <= 0) {
            return ResponseResult.okResult(new ArrayList<>());
        }
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Menu::getId, mIds);
        wrapper.orderByAsc(Menu::getOrderNum);
        wrapper.eq(Menu::getStatus, SystemContains.STATUS_NORMAL);
        wrapper.in(Menu::getMenuType, SystemContains.CATALOGUE, SystemContains.MENU);
        List<Menu> menus = menuService.list(wrapper);
        List<RouterVo> routerVos = BeanCopyUtils.copyBeanList(menus, RouterVo.class);
        routerVos = TreeUtils.generateTrees2(routerVos);
        return ResponseResult.okResult(routerVos);
    }

    @Override
    public ResponseResult registerUser(UserDto userDto) {
        if (!StringUtils.hasText(userDto.getUserName())) {
            throw new SystemException(AppHttpCodeEnum.REQUIRE_USERNAME);
        } else if (!StringUtils.hasText(userDto.getPassword())) {
            throw new SystemException(AppHttpCodeEnum.REQUIRE_PASSWORD);
        } else if (!StringUtils.hasText(userDto.getConfirmPassword())) {
            throw new SystemException(AppHttpCodeEnum.REQUIRE_CONFIRM_PASSWORD);
        } else if (!StringUtils.hasText(userDto.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.REQUIRE_EMAIL);
        } else if (!StringUtils.hasText(userDto.getAccessCode())) {
            throw new SystemException(AppHttpCodeEnum.REQUIRE_CODE);
        }
        if (!userDto.getPassword().equals(userDto.getConfirmPassword())) {
            throw new SystemException(AppHttpCodeEnum.CONFIRM_PASSWORD_ERROR);
        }
        String code = redisCache.getCacheObject(RedisKeyEnum.HT_MAIL_KEY + userDto.getEmail());
        if (!code.equals(userDto.getAccessCode())) {
            throw new SystemException(AppHttpCodeEnum.CHECK_CODE_ERROR);
        }
        User user = BeanCopyUtils.copyBean(userDto, User.class);
        user.setName("用户_" + user.getUserName());
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        userService.save(user);
        // 赋予用户权限
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        if (user.getType().equals(SystemContains.USER_ADMIN)) {
            userRole.setRoleId(SystemContains.BASE_ROLE_ADMIN);
        } else if (user.getType().equals(SystemContains.USER_DOCTOR)) {
            userRole.setRoleId(SystemContains.BASE_ROLE_DOCTOR);
        } else {
            userRole.setRoleId(SystemContains.BASE_ROLE_STUDENT);
        }
        userRoleService.save(userRole);
        return ResponseResult.okResult();
    }
}
